﻿namespace Fiddler
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Configuration;
    using System.Diagnostics;
    using System.Net;
    using System.Net.Security;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    using System.Threading;
    using System.Windows.Forms;
    using Xceed.Compression.Formats;
    using Xceed.Zip;

    public class FiddlerApplication
    {
        internal static AutoResponder _AutoResponder;
        private static bool _bSuppressReportUpdates = false;
        internal static frmViewer _frmMain;
        internal static SplashScreen _frmSplash;
        internal static int _iShowOnlyPID;
        internal static Logger _Log = new Logger(true);
        internal static PreferenceBag _Prefs = null;
        public static bool isClosing;
        internal static readonly PeriodicWorker Janitor = new PeriodicWorker();
        public static X509Certificate oDefaultClientCertificate;
        [CodeDescription("Fiddler's loaded extensions.")]
        public static FiddlerExtensions oExtensions;
        internal static Inspectors oInspectors;
        [CodeDescription("Fiddler's core proxy engine.")]
        public static Proxy oProxy;
        public static FiddlerTranscoders oTranscoders = new FiddlerTranscoders();
        internal static Report Reporter;
        public static FiddlerScript scriptRules;

        public static event SessionStateHandler AfterSessionComplete;

        [CodeDescription("This event fires before the user inspects a session.")]
        public static event CancelEventHandler BeforeInspectSession;

        public static event SessionStateHandler BeforeRequest;

        public static event SessionStateHandler BeforeResponse;

        public static event SessionStateHandler BeforeReturningError;

        [CodeDescription("Sync this event to capture the CalculateReport event, summarizing the selected sessions.")]
        public static event CalculateReportHandler CalculateReport;

        [CodeDescription("Sync this event to be notified when FiddlerCore has attached as the system proxy.")]
        public static event SimpleEventHandler FiddlerAttach;

        [CodeDescription("Sync this event to be notified when Fiddler has completed startup.")]
        public static event SimpleEventHandler FiddlerBoot;

        [CodeDescription("Sync this event to be notified when FiddlerCore has detached as the system proxy.")]
        public static event SimpleEventHandler FiddlerDetach;

        [CodeDescription("Sync this event to be notified when Fiddler shuts down.")]
        public static event SimpleEventHandler FiddlerShutdown;

        [CodeDescription("This event fires when the user instructs Fiddler to clear the cache or cookies.")]
        public static event EventHandler<CacheClearEventArgs> OnClearCache;

        [CodeDescription("This event fires when a user notification would be shown. See CONFIG.QuietMode property.")]
        public static event EventHandler<NotificationEventArgs> OnNotification;

        [CodeDescription("This event fires a HTTPS certificate is validated.")]
        public static event EventHandler<ValidateServerCertificateEventArgs> OnValidateServerCertificate;

        public static event SessionStateHandler RequestHeadersAvailable;

        public static event SessionStateHandler ResponseHeadersAvailable;

        static FiddlerApplication()
        {
            _Prefs = new PreferenceBag(CONFIG.GetRegPath("Prefs"));
        }

        private FiddlerApplication()
        {
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        internal static void _SetXceedLicenseKeys()
        {
            try
            {
                Xceed.Zip.Licenser.LicenseKey = "ZIN53GEWWB8AZ1ANUBA";
            }
            catch (Exception exception)
            {
                ReportException(exception, "Failed to set license key for Xceed.ZIP");
            }
            try
            {
                Xceed.Compression.Formats.Licenser.LicenseKey = "FTN53TEWWB8AZLNPUNA";
            }
            catch (Exception exception2)
            {
                ReportException(exception2, "Failed to set license key for Xceed.Compression");
            }
        }

        [CodeDescription("Notify the user that an event has occured; shows a balloon tip if Fiddler is minimized to the system tray.")]
        public static void AlertUser(string sTitle, string sMessage)
        {
            if ((_frmMain != null) && (_frmMain.notifyIcon != null))
            {
                _frmMain.notifyIcon.ShowBalloonTip(0x3e8, sTitle, sMessage, ToolTipIcon.Info);
            }
        }

        internal static void CheckOverrideCertificatePolicy(Session oS, string sExpectedCN, X509Certificate ServerCertificate, X509Chain ServerCertificateChain, SslPolicyErrors sslPolicyErrors, ref CertificateValidity oValidity)
        {
            EventHandler<ValidateServerCertificateEventArgs> onValidateServerCertificate = OnValidateServerCertificate;
            if (onValidateServerCertificate != null)
            {
                ValidateServerCertificateEventArgs e = new ValidateServerCertificateEventArgs(oS, sExpectedCN, ServerCertificate, ServerCertificateChain, sslPolicyErrors);
                onValidateServerCertificate(oS, e);
                oValidity = e.ValidityState;
            }
        }

        internal static void DebugSpew(string sMessage)
        {
            if (CONFIG.bDebugSpew)
            {
                Trace.WriteLine(sMessage);
            }
        }

        internal static void DoAfterSessionComplete(Session oSession)
        {
            if (AfterSessionComplete != null)
            {
                AfterSessionComplete(oSession);
            }
        }

        internal static bool DoBeforeInspect(Session oSession)
        {
            if (BeforeInspectSession != null)
            {
                CancelEventArgs e = new CancelEventArgs();
                BeforeInspectSession(oSession, e);
                return !e.Cancel;
            }
            return true;
        }

        internal static void DoBeforeRequest(Session oSession)
        {
            if (BeforeRequest != null)
            {
                BeforeRequest(oSession);
            }
        }

        internal static void DoBeforeResponse(Session oSession)
        {
            if (BeforeResponse != null)
            {
                BeforeResponse(oSession);
            }
        }

        internal static void DoBeforeReturningError(Session oSession)
        {
            if (BeforeReturningError != null)
            {
                BeforeReturningError(oSession);
            }
            oExtensions.DoBeforeReturningError(oSession);
        }

        internal static bool DoClearCache(bool bClearFiles, bool bClearCookies)
        {
            EventHandler<CacheClearEventArgs> onClearCache = OnClearCache;
            if (onClearCache == null)
            {
                return true;
            }
            CacheClearEventArgs e = new CacheClearEventArgs(bClearFiles, bClearCookies);
            onClearCache(null, e);
            return !e.Cancel;
        }

        [CodeDescription("Switch to, and fill the Request Composer tab with a copy of the request from the supplied Session.")]
        public static bool DoComposeByCloning(Session oSession)
        {
            if (UIComposer.FillUIFromSession(oSession))
            {
                UIComposer.EnsureShowing();
                return true;
            }
            return false;
        }

        [CodeDescription("Switch to, and fill the Request Composer tab with the Request information supplied.")]
        public static bool DoComposeFrom(HTTPRequestHeaders oRH, byte[] arrRequestBody)
        {
            Session oSession = new Session(oRH, arrRequestBody);
            return DoComposeByCloning(oSession);
        }

        public static bool DoExport(string sExportFormat, Session[] oSessions, Dictionary<string, object> dictOptions, EventHandler<ProgressCallbackEventArgs> ehPCEA)
        {
            if (string.IsNullOrEmpty(sExportFormat))
            {
                return false;
            }
            TranscoderTuple tuple = oTranscoders.GetExporter(sExportFormat);
            if (tuple == null)
            {
                return false;
            }
            bool flag = false;
            try
            {
                UI.UseWaitCursor = true;
                ISessionExporter exporter = (ISessionExporter)Activator.CreateInstance(tuple.typeFormatter);
                if (ehPCEA == null)
                {
                    ehPCEA = delegate(object sender, ProgressCallbackEventArgs oPCE)
                    {
                        string str = (oPCE.PercentComplete > 0) ? ("Export is " + oPCE.PercentComplete + "% complete; ") : string.Empty;
                        Log.LogFormat("{0}{1}", new object[] { str, oPCE.ProgressText });
                        Application.DoEvents();
                    };
                }
                flag = exporter.ExportSessions(sExportFormat, oSessions, dictOptions, ehPCEA);
                exporter.Dispose();
            }
            catch (Exception exception)
            {
                LogAddonException(exception, "Exporter for " + sExportFormat + " failed.");
                flag = false;
            }
            UI.UseWaitCursor = false;
            return flag;
        }

        public static Session[] DoImport(string sImportFormat, bool bAddToSessionList, Dictionary<string, object> dictOptions, EventHandler<ProgressCallbackEventArgs> ehPCEA)
        {
            Session[] sessionArray;
            if (string.IsNullOrEmpty(sImportFormat))
            {
                return null;
            }
            TranscoderTuple tuple = oTranscoders.GetImporter(sImportFormat);
            if (tuple == null)
            {
                return null;
            }
            try
            {
                UI.UseWaitCursor = true;
                ISessionImporter importer = (ISessionImporter)Activator.CreateInstance(tuple.typeFormatter);
                if (ehPCEA == null)
                {
                    ehPCEA = delegate(object sender, ProgressCallbackEventArgs oPCE)
                    {
                        string str = (oPCE.PercentComplete > 0) ? ("Import is " + oPCE.PercentComplete + "% complete; ") : string.Empty;
                        Log.LogFormat("{0}{1}", new object[] { str, oPCE.ProgressText });
                        Application.DoEvents();
                    };
                }
                sessionArray = importer.ImportSessions(sImportFormat, dictOptions, ehPCEA);
                importer.Dispose();
                if (sessionArray == null)
                {
                    UI.UseWaitCursor = false;
                    return null;
                }
                if (bAddToSessionList)
                {
                    try
                    {
                        UI.lvSessions.BeginUpdate();
                        foreach (Session session in sessionArray)
                        {
                            session.SetBitFlag(SessionFlags.ImportedFromOtherTool, true);
                            UI.finishSession(session);
                        }
                    }
                    finally
                    {
                        UI.lvSessions.EndUpdate();
                    }
                }
            }
            catch (Exception exception)
            {
                LogAddonException(exception, "Importer for " + sImportFormat + " failed.");
                sessionArray = null;
            }
            UI.UseWaitCursor = false;
            return sessionArray;
        }

        internal static void DoNotifyUser(string sMessage, string sTitle)
        {
            DoNotifyUser(sMessage, sTitle, MessageBoxIcon.None);
        }

        internal static void DoNotifyUser(string sMessage, string sTitle, MessageBoxIcon oIcon)
        {
            if (OnNotification != null)
            {
                NotificationEventArgs e = new NotificationEventArgs(string.Format("{0} - {1}", sTitle, sMessage));
                OnNotification(null, e);
            }
            if (!CONFIG.QuietMode)
            {
                MessageBox.Show(sMessage, sTitle, MessageBoxButtons.OK, oIcon);
            }
        }

        internal static void DoRequestHeadersAvailable(Session oSession)
        {
            if (RequestHeadersAvailable != null)
            {
                RequestHeadersAvailable(oSession);
            }
            oExtensions.DoPeekAtRequestHeaders(oSession);
        }

        internal static void DoResponseHeadersAvailable(Session oSession)
        {
            if (ResponseHeadersAvailable != null)
            {
                ResponseHeadersAvailable(oSession);
            }
            oExtensions.DoPeekAtResponseHeaders(oSession);
        }

        public static string GetDetailedInfo()
        {
            StringBuilder builder = new StringBuilder(0x200);
            builder.AppendFormat("\nRunning {0}on: {1}:{2}\n", Utilities.IsUserAnAdmin() ? "ELEVATED " : string.Empty, CONFIG.sMachineName, oProxy.ListenPort.ToString());
            if (CONFIG.bHookAllConnections)
            {
                builder.AppendLine("Listening to: All Adapters");
            }
            else
            {
                builder.AppendFormat("Listening to: {0}\n", CONFIG.sHookConnectionNamed ?? "Default LAN");
            }
            if (CONFIG.iReverseProxyForPort > 0)
            {
                builder.AppendFormat("Acting as reverse proxy for port #{0}\n", CONFIG.iReverseProxyForPort);
            }
            if (oProxy.oAutoProxy != null)
            {
                builder.AppendFormat("Gateway: Auto-Config\n{0}", oProxy.oAutoProxy.ToString());
            }
            else
            {
                IPEndPoint point = oProxy.FindGatewayForOrigin("http", "www.fiddler2.com");
                if (point != null)
                {
                    builder.AppendFormat("Gateway: {0}:{1}\n", point.Address.ToString(), point.Port.ToString());
                }
                else
                {
                    builder.AppendLine("Gateway: No Gateway");
                }
            }
            string str = string.Empty;
            try
            {
                str = string.Format("Built: {0}\n", ((FiddlerBuildDate)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(FiddlerBuildDate), false)[0]).ToString());
            }
            catch
            {
            }
            return string.Format("Fiddler Web Debugger ({0})\n{9}\n{1}-bit {2}, VM: {3:N2}mb, WS: {4:N2}mb\n{5} {6}\n\nYou've run Fiddler: {7:N0} times.\n{8}\n", new object[] { CONFIG.bIsBeta ? string.Format("v{0} beta", Application.ProductVersion) : string.Format("v{0}", Application.ProductVersion), (8 == IntPtr.Size) ? "64" : "32", Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"), Process.GetCurrentProcess().PagedMemorySize64 / 0x100000L, Process.GetCurrentProcess().WorkingSet64 / 0x100000L, ".NET " + Environment.Version, Utilities.GetOSVerString(), CONFIG.iStartupCount, builder.ToString(), str });
        }

        public static string GetVersionString()
        {
            FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);
            string str = " (+SAZ)";
            string str2 = "Fiddler";
            return string.Format("{0}/{1}.{2}.{3}.{4}{5}", new object[] { str2, versionInfo.FileMajorPart, versionInfo.FileMinorPart, versionInfo.FileBuildPart, versionInfo.FilePrivatePart, str });
        }

        internal static void HandleHTTPError(Session oSession, SessionFlags flagViolation, bool bPoisonClientConnection, bool bPoisonServerConnection, string sMessage)
        {
            if (bPoisonClientConnection)
            {
                oSession.PoisonClientPipe();
            }
            if (bPoisonServerConnection)
            {
                oSession.PoisonServerPipe();
            }
            oSession.SetBitFlag(flagViolation, true);
            oSession["ui-backcolor"] = "LightYellow";
            if (CONFIG.bReportHTTPErrors && !CONFIG.QuietMode)
            {
                oSession.oFlags.Remove("ui-hide");
                UINotifyList.ReportHTTPError(oSession.id, sMessage);
            }
            Log.LogFormat("{0} - [#{1}] {2}", new object[] { "Fiddler.Network.ProtocolViolation", oSession.id.ToString(), sMessage });
            sMessage = "[ProtocolViolation] " + sMessage;
            if ((oSession["x-HTTPProtocol-Violation"] == null) || !oSession["x-HTTPProtocol-Violation"].Contains(sMessage))
            {
                Session session;
                (session = oSession)["x-HTTPProtocol-Violation"] = session["x-HTTPProtocol-Violation"] + sMessage;
            }
        }

        internal static void LogAddonException(Exception eX, string sTitle)
        {
            if (Prefs.GetBoolPref("fiddler.debug.extensions.showerrors", false) || Prefs.GetBoolPref("fiddler.debug.extensions.verbose", false))
            {
                ReportException(eX, sTitle, "Fiddler has encountered an unexpected problem with an extension.");
            }
        }

        internal static void OnCalculateReport(Session[] _arrSessions)
        {
            if ((!_bSuppressReportUpdates && !isClosing) && (CalculateReport != null))
            {
                CalculateReport(_arrSessions);
            }
        }

        internal static void OnFiddlerAttach()
        {
            _frmMain.miCaptureEnabled.Checked = true;
            _frmMain.miNotifyCapturing.Checked = true;
            if (scriptRules != null)
            {
                scriptRules.DoOnAttach();
            }
            if (FiddlerAttach != null)
            {
                FiddlerAttach();
            }
        }

        internal static void OnFiddlerBoot()
        {
            if (scriptRules != null)
            {
                scriptRules.DoOnBoot();
            }
            if (FiddlerBoot != null)
            {
                FiddlerBoot();
            }
            _Log.FlushStartupMessages();
        }

        internal static void OnFiddlerDetach()
        {
            _frmMain.miCaptureEnabled.Checked = false;
            _frmMain.miNotifyCapturing.Checked = false;
            if (scriptRules != null)
            {
                scriptRules.DoOnDetach();
            }
            if (FiddlerDetach != null)
            {
                FiddlerDetach();
            }
        }

        internal static void OnFiddlerShutdown()
        {
            if (scriptRules != null)
            {
                scriptRules.DoOnShutdown();
            }
            if (FiddlerShutdown != null)
            {
                FiddlerShutdown();
            }
        }

        internal static void ReportException(Exception eX)
        {
            ReportException(eX, "Sorry, you may have found a bug...", null);
        }

        public static void ReportException(Exception eX, string sTitle)
        {
            ReportException(eX, sTitle, null);
        }

        public static void ReportException(Exception eX, string sTitle, string sCallerMessage)
        {
            Trace.WriteLine(string.Concat(new object[] { "*************************\n", eX.Message, "\n", eX.StackTrace, "\n", eX.InnerException }));
            if (!(eX is ThreadAbortException) || !isClosing)
            {
                if (eX is ConfigurationErrorsException)
                {
                    DoNotifyUser(string.Concat(new object[] { 
                        "Your Microsoft .NET Configuration file is corrupt and contains invalid data. You can often correct this error by installing updates from WindowsUpdate and/or reinstalling the .NET Framework.\r\n", eX.Message, "\n\nType: ", eX.GetType().ToString(), "\nSource: ", eX.Source, "\n", eX.StackTrace, "\n\n", eX.InnerException, "\nFiddler v", Application.ProductVersion, (8 == IntPtr.Size) ? " (x64 " : " (x86 ", Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"), ") [.NET ", Environment.Version, 
                        " on ", Environment.OSVersion.VersionString, "] "
                     }), sTitle, MessageBoxIcon.Hand);
                }
                else
                {
                    string str;
                    if (eX is OutOfMemoryException)
                    {
                        sTitle = "Out of Memory Error";
                        str = "An out-of-memory exception was encountered. To help avoid out-of-memory conditions, please see: " + CONFIG.GetUrl("REDIR") + "FIDDLEROOM";
                    }
                    else if (string.IsNullOrEmpty(sCallerMessage))
                    {
                        str = "Fiddler has encountered an unexpected problem. If you believe this is a bug in Fiddler, please copy this message by hitting CTRL+C, and submit a bug report using the Help | Send Feedback menu.";
                    }
                    else
                    {
                        str = sCallerMessage;
                    }
                    DoNotifyUser(string.Concat(new object[] { 
                        str, "\n\n", eX.Message, "\n\nType: ", eX.GetType().ToString(), "\nSource: ", eX.Source, "\n", eX.StackTrace, "\n\n", eX.InnerException, "\nFiddler v", Application.ProductVersion, (8 == IntPtr.Size) ? " (x64 " : " (x86 ", Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE"), ") [.NET ", 
                        Environment.Version, " on ", Environment.OSVersion.VersionString, "] "
                     }), sTitle, MessageBoxIcon.Hand);
                }
            }
        }

        [CodeDescription("Reset the SessionID counter to 0. This method can lead to confusing UI, so call sparingly.")]
        public static void ResetSessionCounter()
        {
            Session.ResetSessionCounter();
        }

        internal static void UIInvoke(MethodInvoker target)
        {
            if (!isClosing)
            {
                if (_frmMain.InvokeRequired)
                {
                    _frmMain.Invoke(target);
                }
                else
                {
                    target();
                }
            }
        }

        internal static void UIInvokeAsync(Delegate oDel, object[] args)
        {
            if (!isClosing)
            {
                _frmMain.BeginInvoke(oDel, args);
            }
        }

        [CodeDescription("Fiddler's logging subsystem; displayed on the LOG tab by default.")]
        public static Logger Log
        {
            get
            {
                return _Log;
            }
        }

        [CodeDescription("Fiddler's AutoResponder object.")]
        public static AutoResponder oAutoResponder
        {
            get
            {
                return _AutoResponder;
            }
        }

        [CodeDescription("Fiddler's Preferences collection. http://fiddler.wikidot.com/prefs")]
        public static IFiddlerPreferences Prefs
        {
            get
            {
                return _Prefs;
            }
        }

        internal static bool SuppressReportUpdates
        {
            get
            {
                return _bSuppressReportUpdates;
            }
            set
            {
                _bSuppressReportUpdates = value;
                if (!_bSuppressReportUpdates)
                {
                    _frmMain.actReportStatistics(true);
                }
            }
        }

        [CodeDescription("Fiddler's main form.")]
        public static frmViewer UI
        {
            get
            {
                return _frmMain;
            }
        }
    }
}

